
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       dev_mgr.h
  * @author     baiyang
  * @date       2021-10-20
  ******************************************************************************
  */

#pragma once

#ifdef __cplusplus
extern "C"{
#endif

/*----------------------------------include-----------------------------------*/
#include <stdint.h>
#include <stdbool.h>

#include <rtthread.h>
#include <rtdevice.h>

#include <board/mb_sensor_config.h>
/*-----------------------------------macro------------------------------------*/
#ifndef MHZ
#define MHZ (1000U*1000U)
#endif

#ifndef KHZ
#define KHZ (1000U)
#endif

/* InertialSensor driver types */
#define HAL_INS_NONE         0
#define HAL_INS_MPU60XX_SPI  2
#define HAL_INS_MPU60XX_I2C  3
#define HAL_INS_HIL_UNUSED   4  // unused
#define HAL_INS_VRBRAIN      8
#define HAL_INS_MPU9250_SPI  9
#define HAL_INS_MPU9250_I2C 13
#define HAL_INS_MPU6500     19
#define HAL_INS_INV2_I2C    24
#define HAL_INS_INV2_SPI    25


/* Barometer driver types */
#define HAL_BARO_NONE        0
#define HAL_BARO_HIL_UNUSED  6  // unused
#define HAL_BARO_20789_I2C_I2C  14
#define HAL_BARO_20789_I2C_SPI  15
#define HAL_BARO_LPS25H_IMU_I2C 17

/* Compass driver types */
#define HAL_COMPASS_NONE                0

// by default assume first I2C bus is internal
#ifndef HAL_I2C_INTERNAL_MASK
#define HAL_I2C_INTERNAL_MASK 1
#endif

// spi1的ID是0
#define SPI1_BUS_ID 0U
#define SPI1_BUS_ID 0U
#define SPI1_BUS_ID 0U
#define SPI1_BUS_ID 0U

// spi2的ID是1
#define SPI2_BUS_ID 1U
#define SPI2_BUS_ID 1U
#define SPI2_BUS_ID 1U
#define SPI2_BUS_ID 1U

// spi3的ID是2
#define SPI3_BUS_ID 2U
#define SPI3_BUS_ID 2U
#define SPI3_BUS_ID 2U
#define SPI3_BUS_ID 2U

// spi4的ID是3
#define SPI4_BUS_ID 3U
#define SPI4_BUS_ID 3U
#define SPI4_BUS_ID 3U
#define SPI4_BUS_ID 3U

#ifndef SPI_DEVICE_NAME_NONE
#define SPI_DEVICE_NAME_NONE " "
#endif

#if defined(HAL_RTTHREAD_ARCH_FMUV2)
#define MPU60x0_SPI_DEVICE_NAME    SPI1_D4_NAME
#define L3GD20H_SPI_DEVICE_NAME    SPI1_D2_NAME
#define MS5611_SPI_DEVICE_NAME     SPI1_D3_NAME
#define LSM9DS0_SPI_DEVICE_NAME    SPI1_D1_NAME
#elif defined(HAL_RTTHREAD_ARCH_FMUV5)
#define ICM20689_SPI_DEVICE_NAME   SPI1_D1_NAME
#define ICM20602_SPI_DEVICE_NAME   SPI1_D2_NAME
#define MS5611_SPI_DEVICE_NAME     SPI4_D1_NAME
#define BMI055_G_SPI_DEVICE_NAME   SPI1_D3_NAME
#define BMI055_A_SPI_DEVICE_NAME   SPI1_D4_NAME
#elif defined(HAL_RTTHREAD_ARCH_AP0T)
#define ICM20602_SPI_DEVICE_NAME   SPI1_D4_NAME
#define MS5611_SPI_DEVICE_NAME     SPI1_D5_NAME
#define BMI055_G_SPI_DEVICE_NAME   SPI1_D1_NAME
#define BMI055_A_SPI_DEVICE_NAME   SPI1_D2_NAME
#elif defined(HAL_RTTHREAD_ARCH_APMCNV1)
#define QMI8658_SPI_DEVICE_NAME    SPI1_D3_NAME
#elif defined(HAL_RTTHREAD_ARCH_APGDSTV1)
#define QMI8658_SPI_DEVICE_NAME    SPI1_D1_NAME
#define ICM20602_SPI_DEVICE_NAME   SPI1_D2_NAME
#define MS5611_SPI_DEVICE_NAME     SPI3_D3_NAME
#endif


#ifndef ICM20608_SPI_DEVICE_NAME
#define ICM20608_SPI_DEVICE_NAME   SPI_DEVICE_NAME_NONE
#endif

#ifndef ICM20689_SPI_DEVICE_NAME
#define ICM20689_SPI_DEVICE_NAME   SPI_DEVICE_NAME_NONE
#endif

#ifndef ICM20602_SPI_DEVICE_NAME
#define ICM20602_SPI_DEVICE_NAME   SPI_DEVICE_NAME_NONE
#endif

#ifndef MPU9250_SPI_DEVICE_NAME
#define MPU9250_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

#ifndef MPU60x0_SPI_DEVICE_NAME
#define MPU60x0_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

#ifndef BMI055_G_SPI_DEVICE_NAME
#define BMI055_G_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

#ifndef BMI055_A_SPI_DEVICE_NAME
#define BMI055_A_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

#ifndef L3GD20H_SPI_DEVICE_NAME
#define L3GD20H_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

#ifndef MS5611_SPI_DEVICE_NAME
#define MS5611_SPI_DEVICE_NAME     SPI_DEVICE_NAME_NONE
#endif

#ifndef LSM9DS0_SPI_DEVICE_NAME
#define LSM9DS0_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

#ifndef HAL_COMPASS_HMC5843_NAME
#define HAL_COMPASS_HMC5843_NAME   SPI_DEVICE_NAME_NONE
#endif

#ifndef QMI8658_SPI_DEVICE_NAME
#define QMI8658_SPI_DEVICE_NAME    SPI_DEVICE_NAME_NONE
#endif

/*----------------------------------typedef-----------------------------------*/
typedef bool (*BankSelectCb)(uint8_t);

enum DeviceBusType {
    BUS_TYPE_UNKNOWN = 0,
    BUS_TYPE_I2C     = 1,
    BUS_TYPE_SPI     = 2,
    BUS_TYPE_UAVCAN  = 3,
    BUS_TYPE_SITL    = 4,
    BUS_TYPE_MSP     = 5,
    BUS_TYPE_SERIAL  = 6,
    BUS_TYPE_QSPI    = 7,
};

enum DeviceSpeed {
    DEV_SPEED_HIGH,
    DEV_SPEED_LOW,
};

struct DeviceStructure {
    uint8_t bus_type : 3;  // enum BusType
    uint8_t bus : 5;       // which instance of the bus type
    uint8_t address;   // address on the bus (eg. I2C address)
    uint8_t devtype;   // device class specific device type
};

union DeviceId {
    struct DeviceStructure devid_s;
    uint32_t devid;
};

struct DeviceCallbackInfo {
    struct DeviceCallbackInfo *next;
    void (*cb)(void *parameter);
    void *parameter;
    uint32_t period_usec;
    uint64_t next_usec;
    struct gp_device* bus;
};

// checked registers
struct checkreg {
    uint8_t bank;
    uint8_t regnum;
    uint8_t value;
};

struct device_checked{
    uint8_t n_allocated;
    uint8_t n_set;
    uint8_t next;
    uint8_t frequency;
    uint8_t counter;
    struct checkreg last_reg_fail;
    struct checkreg *regs;
};

typedef struct gp_device* gp_device_t;
typedef struct device_bus* device_bus_t;

struct device_bus
{
    uint8_t bus_id;
    struct DeviceCallbackInfo* bus_callbacks;
    bool thread_started;
    rt_thread_t thread_ctx;
};

struct gp_device
{
    rt_device_t dev;

    struct gp_device *next;

    struct devmgr_device_ops *ops;

    union DeviceId d;

    uint8_t _read_flag;
    BankSelectCb _bank_select;
    struct device_checked _checked;

    device_bus_t bus;

    // SPI配置
    uint8_t spi_data_width;
    uint8_t spi_mode;
    uint32_t spi_freq_low;
    uint32_t spi_freq_high;

    /* I2C interface #2 */
    uint8_t _retries;
    uint8_t _address;
    bool _split_transfers;
    bool _use_smbus;
    uint32_t _timeout_ms;
};

/** @ 
  * @brief  
  */
struct devmgr_device_ops {
    /**
     * Wrapper function over #transfer() to read recv_len registers, starting
     * by first_reg, into the array pointed by recv. The read flag passed to
     * #set_read_flag(uint8_t) is ORed with first_reg before performing the
     * transfer.
     *
     * Return: true on a successful transfer, false on failure.
     */
    bool (*read_registers)(gp_device_t dev, uint8_t first_reg, uint8_t *recv, uint32_t recv_len);

    /**
    * Wrapper function over #transfer() to write a byte to the register reg.
    * The transfer is done by sending reg and val in that order.
    *
    * Return: true on a successful transfer, false on failure.
    */
    bool (*write_register)(gp_device_t dev, uint8_t reg, uint8_t val, bool checked);

    bool (*transfer)(gp_device_t dev, const uint8_t *send, uint32_t send_len, uint8_t *recv, uint32_t recv_len);

    bool (*take_bus)(gp_device_t dev);
    bool (*release_bus)(gp_device_t device);
};

/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
gp_device_t devmgr_get_spi_device(const char *name);
gp_device_t devmgr_get_i2c_device(uint8_t bus, uint8_t address);

static inline bool devmgr_take_bus(gp_device_t dev) {
    if (dev->ops != NULL) {
        return dev->ops->take_bus(dev);
    }

    return false;
}

static inline bool devmgr_release_bus(gp_device_t dev) {
    if (dev->ops != NULL) {
        return dev->ops->release_bus(dev);
    }

    return false;
}

static inline enum DeviceBusType devmgr_get_bus_type(gp_device_t device) { return device->d.devid_s.bus_type; }

// set device type within a device class (eg. COMPASS_TYPE_LSM303D)
static inline void devmgr_set_device_type(gp_device_t dev, uint8_t devtype) { dev->d.devid_s.devtype = devtype; }
uint32_t devmgr_get_bus_id_devtype(gp_device_t dev, uint8_t devtype);
static inline uint32_t devmgr_get_bus_id(gp_device_t dev){ RT_ASSERT(dev != RT_NULL); return dev->d.devid; }

bool devmgr_set_speed(gp_device_t dev, enum DeviceSpeed speed);
void devmgr_set_read_flag(gp_device_t dev, uint8_t flag);

static inline bool devmgr_read_registers(gp_device_t dev, uint8_t first_reg, uint8_t *recv, uint32_t recv_len)
{
    if (dev->ops != NULL) {
        return dev->ops->read_registers(dev, first_reg, recv, recv_len);
    }

    return false;
}

// checked, defult false
static inline bool devmgr_write_register(gp_device_t dev, uint8_t reg, uint8_t val, bool checked)
{
    if (dev->ops != NULL) {
        return dev->ops->write_register(dev, reg, val, checked);
    }

    return false;
}

static inline bool devmgr_transfer(gp_device_t dev, const uint8_t *send, uint32_t send_len, uint8_t *recv, uint32_t recv_len)
{
    if (dev->ops != NULL) {
        return dev->ops->transfer(dev, send, send_len, recv, recv_len);
    }

    return false;
}

bool devmgr_transfer(gp_device_t dev, const uint8_t *send, uint32_t send_len, uint8_t *recv, uint32_t recv_len);
bool devmgr_write_register(gp_device_t dev, uint8_t reg, uint8_t val, bool checked);
struct DeviceCallbackInfo *devmgr_register_periodic_callback(gp_device_t dev, uint32_t period_usec, void (*cb)(void *parameter), void *parameter);
bool devmgr_adjust_periodic_callback(gp_device_t dev, struct DeviceCallbackInfo *callback, uint32_t period_usec);

bool devmgr_setup_checked_registers(gp_device_t dev, uint8_t nregs, uint8_t frequency);
void devmgr_set_checked_register2(gp_device_t dev, uint8_t bank, uint8_t reg, uint8_t val);
static inline void devmgr_set_checked_register(gp_device_t dev, uint8_t reg, uint8_t val) { devmgr_set_checked_register2(dev, 0, reg, val); }
bool devmgr_check_next_register(gp_device_t dev);
bool devmgr_check_next_register2(gp_device_t dev, struct checkreg *fail);

/**
 * make a bus id given bus type, bus number, bus address and
 * device type This is for use by devices that do not use one of
 * the standard HAL Device types, such as UAVCAN devices
 */
static inline uint32_t devmgr_make_bus_id(enum DeviceBusType bus_type, uint8_t bus, uint8_t address, uint8_t devtype) {
    union DeviceId d = {0};
    d.devid_s.bus_type = bus_type;
    d.devid_s.bus = bus;
    d.devid_s.address = address;
    d.devid_s.devtype = devtype;
    return d.devid;
}

/**
 * return a new bus ID for the same bus connection but a new device type.
 * This is used for auxillary bus connections
 */
static inline uint32_t devmgr_change_bus_id(uint32_t old_id, uint8_t devtype) {
    union DeviceId d;
    d.devid = old_id;
    d.devid_s.devtype = devtype;
    return d.devid;
}

/* See AP_HAL::I2CDevice::set_address() */
static inline void devmgr_set_address(gp_device_t dev, uint8_t address) { dev->_address = address; }

/* See AP_HAL::I2CDevice::set_retries() */
static inline void devmgr_set_retries(gp_device_t dev, uint8_t retries) { dev->_retries = retries; }

/// i2c bus
uint32_t devmgr_get_bus_mask(void);
uint32_t devmgr_get_bus_mask_internal(void);
uint32_t devmgr_get_bus_mask_external(void);
/*------------------------------------test------------------------------------*/

#ifdef __cplusplus
}
#endif



